/*-------------------------------------------------------------------------
 *
 * tupdesc.c
 *	  POSTGRES 元组描述符支持代码
 *
 * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 *
 * IDENTIFICATION
 *	  src/backend/access/common/tupdesc.c
 *
 * NOTES
 *	  一些执行器实用代码，例如 "ExecTypeFromTL"，应该移到这里。
 *
 *-------------------------------------------------------------------------
 */

#include "postgres.h"

#include "access/htup_details.h"
#include "access/toast_compression.h"
#include "access/tupdesc_details.h"
#include "catalog/pg_collation.h"
#include "catalog/pg_type.h"
#include "common/hashfn.h"
#include "miscadmin.h"
#include "parser/parse_type.h"
#include "utils/acl.h"
#include "utils/builtins.h"
#include "utils/datum.h"
#include "utils/resowner_private.h"
#include "utils/syscache.h"

/*
 * CreateTemplateTupleDesc
 *		此函数分配一个空的元组描述符结构。
 *
 * 元组类型 ID 信息最初设置为匿名记录类型；调用者可以在需要时覆盖此信息。
 */
TupleDesc CreateTemplateTupleDesc(int fc_natts)
{
	TupleDesc	fc_desc;

	/*
	 * 健康检查
	 */
	AssertArg(fc_natts >= 0);

	/*
	 * 为元组描述符分配足够的内存，包括属性行。
	 *
	 * 注意：属性数组的步幅为 sizeof(FormData_pg_attribute)，
	 * 因为我们为了方便表示将数组元素声明为 FormData_pg_attribute。
	 * 然而，我们只保证每个条目的前 ATTRIBUTE_FIXED_PART_SIZE 字节有效；
	 * 大多数复制 tupdesc 条目的代码只复制那部分。
	 * 原则上，由于尾部填充，这个大小可能会更小，
	 * 尽管在当前 pg_attribute 的定义下，可能没有任何填充。
	 */
	fc_desc = (TupleDesc) palloc(offsetof(struct TupleDescData, attrs) +
							  fc_natts * sizeof(FormData_pg_attribute));

	/*
	 * 初始化 tupdesc 的其他字段。
	 */
	fc_desc->natts = fc_natts;
	fc_desc->constr = NULL;
	fc_desc->tdtypeid = RECORDOID;
	fc_desc->tdtypmod = -1;
	fc_desc->tdrefcount = -1;		/* 假定不进行引用计数 */

	return fc_desc;
}

/*
 * CreateTupleDesc
 *		此函数通过复制给定的 Form_pg_attribute 数组分配一个新的 TupleDesc。
 *
 * 元组类型 ID 信息最初设置为匿名记录类型；调用者可以在需要时覆盖此信息。
 */
TupleDesc CreateTupleDesc(int fc_natts, Form_pg_attribute *fc_attrs)
{
	TupleDesc	fc_desc;
	int			fc_i;

	fc_desc = CreateTemplateTupleDesc(fc_natts);

	for (fc_i = 0; fc_i < fc_natts; ++fc_i)
		memcpy(TupleDescAttr(fc_desc, fc_i), fc_attrs[fc_i], ATTRIBUTE_FIXED_PART_SIZE);

	return fc_desc;
}

/*
 * CreateTupleDescCopy
 *		此函数通过从现有 TupleDesc 复制来创建一个新的 TupleDesc。
 *
 * !!! 约束和默认值不被复制 !!!
 */
TupleDesc CreateTupleDescCopy(TupleDesc fc_tupdesc)
{
	TupleDesc	fc_desc;
	int			fc_i;

	fc_desc = CreateTemplateTupleDesc(fc_tupdesc->natts);

	/* 扁平复制属性数组 */
	memcpy(TupleDescAttr(fc_desc, 0),
		   TupleDescAttr(fc_tupdesc, 0),
		   fc_desc->natts * sizeof(FormData_pg_attribute));

	/*
	 * 由于我们不复制约束和默认值，清除与它们相关的字段。
	 */
	for (fc_i = 0; fc_i < fc_desc->natts; fc_i++)
	{
		Form_pg_attribute fc_att = TupleDescAttr(fc_desc, fc_i);

		fc_att->attnotnull = false;
		fc_att->atthasdef = false;
		fc_att->atthasmissing = false;
		fc_att->attidentity = '\0';
		fc_att->attgenerated = '\0';
	}

	/* 我们也可以复制元组类型标识 */
	fc_desc->tdtypeid = fc_tupdesc->tdtypeid;
	fc_desc->tdtypmod = fc_tupdesc->tdtypmod;

	return fc_desc;
}

/*
 * CreateTupleDescCopyConstr
 *		此函数通过从现有 TupleDesc 复制（包括其约束和默认值）来创建一个新的 TupleDesc。
 */
TupleDesc CreateTupleDescCopyConstr(TupleDesc fc_tupdesc)
{
	TupleDesc	fc_desc;
	TupleConstr *fc_constr = fc_tupdesc->constr;
	int			fc_i;

	fc_desc = CreateTemplateTupleDesc(fc_tupdesc->natts);

	/* 扁平复制属性数组 */
	memcpy(TupleDescAttr(fc_desc, 0),
		   TupleDescAttr(fc_tupdesc, 0),
		   fc_desc->natts * sizeof(FormData_pg_attribute));

	/* 如果有，复制 TupleConstr 数据结构 */
	if (fc_constr)
	{
		TupleConstr *fc_cpy = (TupleConstr *) palloc0(sizeof(TupleConstr));

		fc_cpy->has_not_null = fc_constr->has_not_null;
		fc_cpy->has_generated_stored = fc_constr->has_generated_stored;

		if ((fc_cpy->num_defval = fc_constr->num_defval) > 0)
		{
			fc_cpy->defval = (AttrDefault *) palloc(fc_cpy->num_defval * sizeof(AttrDefault));
			memcpy(fc_cpy->defval, fc_constr->defval, fc_cpy->num_defval * sizeof(AttrDefault));
			for (fc_i = fc_cpy->num_defval - 1; fc_i >= 0; fc_i--)
				fc_cpy->defval[fc_i].adbin = pstrdup(fc_constr->defval[fc_i].adbin);
		}

		if (fc_constr->missing)
		{
			fc_cpy->missing = (AttrMissing *) palloc(fc_tupdesc->natts * sizeof(AttrMissing));
			memcpy(fc_cpy->missing, fc_constr->missing, fc_tupdesc->natts * sizeof(AttrMissing));
			for (fc_i = fc_tupdesc->natts - 1; fc_i >= 0; fc_i--)
			{
				if (fc_constr->missing[fc_i].am_present)
				{
					Form_pg_attribute fc_attr = TupleDescAttr(fc_tupdesc, fc_i);

					fc_cpy->missing[fc_i].am_value = datumCopy(fc_constr->missing[fc_i].am_value,
														 fc_attr->attbyval,
														 fc_attr->attlen);
				}
			}
		}

		if ((fc_cpy->num_check = fc_constr->num_check) > 0)
		{
			fc_cpy->check = (ConstrCheck *) palloc(fc_cpy->num_check * sizeof(ConstrCheck));
			memcpy(fc_cpy->check, fc_constr->check, fc_cpy->num_check * sizeof(ConstrCheck));
			for (fc_i = fc_cpy->num_check - 1; fc_i >= 0; fc_i--)
			{
				fc_cpy->check[fc_i].ccname = pstrdup(fc_constr->check[fc_i].ccname);
				fc_cpy->check[fc_i].ccbin = pstrdup(fc_constr->check[fc_i].ccbin);
				fc_cpy->check[fc_i].ccvalid = fc_constr->check[fc_i].ccvalid;
				fc_cpy->check[fc_i].ccnoinherit = fc_constr->check[fc_i].ccnoinherit;
			}
		}

		fc_desc->constr = fc_cpy;
	}

	/* 我们也可以复制元组类型标识 */
	fc_desc->tdtypeid = fc_tupdesc->tdtypeid;
	fc_desc->tdtypmod = fc_tupdesc->tdtypmod;

	return fc_desc;
}

/*
 * TupleDescCopy
 *		将元组描述符复制到调用者提供的内存中。
 *		内存可以是映射到任何地址的共享内存，并且必须
 *		足够容纳 TupleDescSize(src) 字节。
 *
 * !!! 约束和默认值不被复制 !!!
 */
void TupleDescCopy(TupleDesc fc_dst, TupleDesc fc_src)
{
	int			fc_i;

	/* 扁平复制头部和属性数组 */
	memcpy(fc_dst, fc_src, TupleDescSize(fc_src));

	/*
	 * 由于我们不复制约束和默认值，清除与它们相关的字段。
	 */
	for (fc_i = 0; fc_i < fc_dst->natts; fc_i++)
	{
		Form_pg_attribute fc_att = TupleDescAttr(fc_dst, fc_i);

		fc_att->attnotnull = false;
		fc_att->atthasdef = false;
		fc_att->atthasmissing = false;
		fc_att->attidentity = '\0';
		fc_att->attgenerated = '\0';
	}
	fc_dst->constr = NULL;

	/*
	 * 此外，假设目标不需要进行引用计数。 （在任何情况下复制
	 * 源的引用计数都是错误的。）
	 */
	fc_dst->tdrefcount = -1;
}

/*
 * TupleDescCopyEntry
 *		此函数从一个元组描述符复制单个属性结构到另一个元组描述符。
 *
 * !!! 约束和默认值不被复制 !!!
 */
void TupleDescCopyEntry(TupleDesc fc_dst, AttrNumber fc_dstAttno,
				   TupleDesc fc_src, AttrNumber fc_srcAttno)
{
	Form_pg_attribute fc_dstAtt = TupleDescAttr(fc_dst, fc_dstAttno - 1);
	Form_pg_attribute fc_srcAtt = TupleDescAttr(fc_src, fc_srcAttno - 1);

	/*
	 * 健康检查
	 */
	AssertArg(PointerIsValid(fc_src));
	AssertArg(PointerIsValid(fc_dst));
	AssertArg(fc_srcAttno >= 1);
	AssertArg(fc_srcAttno <= fc_src->natts);
	AssertArg(fc_dstAttno >= 1);
	AssertArg(fc_dstAttno <= fc_dst->natts);

	memcpy(fc_dstAtt, fc_srcAtt, ATTRIBUTE_FIXED_PART_SIZE);

	/*
	 * 除了更新 attno，我们最好重置 attcacheoff。
	 *
	 * XXX 实际上，为了完全安全，我们需要重置 dst 中所有后续列的 attcacheoff。
	 * 但当前的使用场景并不要求这样，因为所有后续列将通过此函数或 TupleDescInitEntry 的其他使用进行初始化。
	 * 所以我们稍微作弊一下，以避免无用的 O(N^2) 罚款。
	 */
	fc_dstAtt->attnum = fc_dstAttno;
	fc_dstAtt->attcacheoff = -1;

	/* 由于我们不复制约束或默认值，因此清除这些 */
	fc_dstAtt->attnotnull = false;
	fc_dstAtt->atthasdef = false;
	fc_dstAtt->atthasmissing = false;
	fc_dstAtt->attidentity = '\0';
	fc_dstAtt->attgenerated = '\0';
}

/*
 * 释放一个包括所有子结构的 TupleDesc
 */
void FreeTupleDesc(TupleDesc fc_tupdesc)
{
	int			fc_i;

	/*
	 * 可能应该断言 tdrefcount == 0，以禁止显式释放未引用计数的 tupdescs？
	 */
	Assert(fc_tupdesc->tdrefcount <= 0);

	if (fc_tupdesc->constr)
	{
		if (fc_tupdesc->constr->num_defval > 0)
		{
			AttrDefault *fc_attrdef = fc_tupdesc->constr->defval;

			for (fc_i = fc_tupdesc->constr->num_defval - 1; fc_i >= 0; fc_i--)
				pfree(fc_attrdef[fc_i].adbin);
			pfree(fc_attrdef);
		}
		if (fc_tupdesc->constr->missing)
		{
			AttrMissing *fc_attrmiss = fc_tupdesc->constr->missing;

			for (fc_i = fc_tupdesc->natts - 1; fc_i >= 0; fc_i--)
			{
				if (fc_attrmiss[fc_i].am_present
					&& !TupleDescAttr(fc_tupdesc, fc_i)->attbyval)
					pfree(DatumGetPointer(fc_attrmiss[fc_i].am_value));
			}
			pfree(fc_attrmiss);
		}
		if (fc_tupdesc->constr->num_check > 0)
		{
			ConstrCheck *fc_check = fc_tupdesc->constr->check;

			for (fc_i = fc_tupdesc->constr->num_check - 1; fc_i >= 0; fc_i--)
			{
				pfree(fc_check[fc_i].ccname);
				pfree(fc_check[fc_i].ccbin);
			}
			pfree(fc_check);
		}
		pfree(fc_tupdesc->constr);
	}

	pfree(fc_tupdesc);
}

/*
 * 增加 tupdesc 的引用计数，并在 CurrentResourceOwner 中记录引用。
 *
 * 不要将其应用于未被引用计数的 tupdescs。（对于状态不确定的 tupdescs，请使用宏 PinTupleDesc。）
 */
void IncrTupleDescRefCount(TupleDesc fc_tupdesc)
{
	Assert(fc_tupdesc->tdrefcount >= 0);

	ResourceOwnerEnlargeTupleDescs(CurrentResourceOwner);
	fc_tupdesc->tdrefcount++;
	ResourceOwnerRememberTupleDesc(CurrentResourceOwner, fc_tupdesc);
}

/*
 * 减少 tupdesc 的引用计数，从 CurrentResourceOwner 移除对应的引用，
 * 如果没有更多引用，则释放 tupdesc。
 *
 * 不要将其应用于未被引用计数的 tupdescs。（对于状态不确定的 tupdescs，请使用宏 ReleaseTupleDesc。）
 */
void DecrTupleDescRefCount(TupleDesc fc_tupdesc)
{
	Assert(fc_tupdesc->tdrefcount > 0);

	ResourceOwnerForgetTupleDesc(CurrentResourceOwner, fc_tupdesc);
	if (--fc_tupdesc->tdrefcount == 0)
		FreeTupleDesc(fc_tupdesc);
}

/*
 * 比较两个 TupleDesc 结构的逻辑相等性
 *
 * 注意：我们故意不检查 attrelid 和 tdtypmod 字段。
 * 这允许 typcache.c 使用此例程来查看缓存的记录类型是否与请求的类型匹配，
 * 并且对于 relcache.c 的使用是无害的。
 * 我们也不比较 tdrefcount。
 */
bool equalTupleDescs(TupleDesc fc_tupdesc1, TupleDesc fc_tupdesc2)
{
	int			fc_i,
				fc_n;

	if (fc_tupdesc1->natts != fc_tupdesc2->natts)
		return false;
	if (fc_tupdesc1->tdtypeid != fc_tupdesc2->tdtypeid)
		return false;

	for (fc_i = 0; fc_i < fc_tupdesc1->natts; fc_i++)
	{
		Form_pg_attribute fc_attr1 = TupleDescAttr(fc_tupdesc1, fc_i);
		Form_pg_attribute fc_attr2 = TupleDescAttr(fc_tupdesc2, fc_i);

		/*
		 * 我们不需要检查每一个字段：我们可以忽略 attrelid 和 attnum
		 * （它们用于最初将行放置在 attrs 数组中）。看起来我们可以不检查 attlen/attbyval/attalign，
		 * 因为这些是从 atttypid 派生的；但在删除列的情况下我们必须检查它们
		 * （因为对于所有删除的列，atttypid 将为零），总的来说，总是检查它们似乎更安全。
		 *
		 * attcacheoff 必须不要检查，因为它可能在两个副本中都未设置。
		 * 我们还故意忽略 atthasmissing，因为在缺少 attmissingval 字段的 tupdescs 中这并不是很相关。
		 */
		if (strcmp(NameStr(fc_attr1->attname), NameStr(fc_attr2->attname)) != 0)
			return false;
		if (fc_attr1->atttypid != fc_attr2->atttypid)
			return false;
		if (fc_attr1->attstattarget != fc_attr2->attstattarget)
			return false;
		if (fc_attr1->attlen != fc_attr2->attlen)
			return false;
		if (fc_attr1->attndims != fc_attr2->attndims)
			return false;
		if (fc_attr1->atttypmod != fc_attr2->atttypmod)
			return false;
		if (fc_attr1->attbyval != fc_attr2->attbyval)
			return false;
		if (fc_attr1->attalign != fc_attr2->attalign)
			return false;
		if (fc_attr1->attstorage != fc_attr2->attstorage)
			return false;
		if (fc_attr1->attcompression != fc_attr2->attcompression)
			return false;
		if (fc_attr1->attnotnull != fc_attr2->attnotnull)
			return false;
		if (fc_attr1->atthasdef != fc_attr2->atthasdef)
			return false;
		if (fc_attr1->attidentity != fc_attr2->attidentity)
			return false;
		if (fc_attr1->attgenerated != fc_attr2->attgenerated)
			return false;
		if (fc_attr1->attisdropped != fc_attr2->attisdropped)
			return false;
		if (fc_attr1->attislocal != fc_attr2->attislocal)
			return false;
		if (fc_attr1->attinhcount != fc_attr2->attinhcount)
			return false;
		if (fc_attr1->attcollation != fc_attr2->attcollation)
			return false;
		/* 可变长度字段甚至不存在... */
	}

	if (fc_tupdesc1->constr != NULL)
	{
		TupleConstr *fc_constr1 = fc_tupdesc1->constr;
		TupleConstr *fc_constr2 = fc_tupdesc2->constr;

		if (fc_constr2 == NULL)
			return false;
		if (fc_constr1->has_not_null != fc_constr2->has_not_null)
			return false;
		if (fc_constr1->has_generated_stored != fc_constr2->has_generated_stored)
			return false;
		fc_n = fc_constr1->num_defval;
		if (fc_n != (int) fc_constr2->num_defval)
			return false;
		/* 我们在这里假设两个 AttrDefault 数组是按 adnum 顺序排列的 */
		for (fc_i = 0; fc_i < fc_n; fc_i++)
		{
			AttrDefault *fc_defval1 = fc_constr1->defval + fc_i;
			AttrDefault *fc_defval2 = fc_constr2->defval + fc_i;

			if (fc_defval1->adnum != fc_defval2->adnum)
				return false;
			if (strcmp(fc_defval1->adbin, fc_defval2->adbin) != 0)
				return false;
		}
		if (fc_constr1->missing)
		{
			if (!fc_constr2->missing)
				return false;
			for (fc_i = 0; fc_i < fc_tupdesc1->natts; fc_i++)
			{
				AttrMissing *fc_missval1 = fc_constr1->missing + fc_i;
				AttrMissing *fc_missval2 = fc_constr2->missing + fc_i;

				if (fc_missval1->am_present != fc_missval2->am_present)
					return false;
				if (fc_missval1->am_present)
				{
					Form_pg_attribute fc_missatt1 = TupleDescAttr(fc_tupdesc1, fc_i);

					if (!datumIsEqual(fc_missval1->am_value, fc_missval2->am_value,
									  fc_missatt1->attbyval, fc_missatt1->attlen))
						return false;
				}
			}
		}
		else if (fc_constr2->missing)
			return false;
		fc_n = fc_constr1->num_check;
		if (fc_n != (int) fc_constr2->num_check)
			return false;

		/*
		 * 同样，我们在这里依赖于 ConstrCheck 条目按名称排序。
		 * 如果有重复名称，比较的结果是不确定的，但这种情况不应发生。
		 */
		for (fc_i = 0; fc_i < fc_n; fc_i++)
		{
			ConstrCheck *fc_check1 = fc_constr1->check + fc_i;
			ConstrCheck *fc_check2 = fc_constr2->check + fc_i;

			if (!(strcmp(fc_check1->ccname, fc_check2->ccname) == 0 &&
				  strcmp(fc_check1->ccbin, fc_check2->ccbin) == 0 &&
				  fc_check1->ccvalid == fc_check2->ccvalid &&
				  fc_check1->ccnoinherit == fc_check2->ccnoinherit))
				return false;
		}
	}
	else if (fc_tupdesc2->constr != NULL)
		return false;
	return true;
}

/*
 * hashTupleDesc
 *		计算一个元组描述符的哈希值。
 *
 * 如果两个元组描述符被 equalTupleDescs() 认为相等，
 * 则根据此函数，它们的哈希值将相等。
 *
 * 注意，目前约束的内容未被哈希 - 这样做可能会有点麻烦，
 * 且由于约束引起的冲突不太可能。
 */
uint32 hashTupleDesc(TupleDesc fc_desc)
{
	uint32		fc_s;
	int			fc_i;

	fc_s = hash_combine(0, hash_uint32(fc_desc->natts));
	fc_s = hash_combine(fc_s, hash_uint32(fc_desc->tdtypeid));
	for (fc_i = 0; fc_i < fc_desc->natts; ++fc_i)
		fc_s = hash_combine(fc_s, hash_uint32(TupleDescAttr(fc_desc, fc_i)->atttypid));

	return fc_s;
}

/*
 * TupleDescInitEntry
 *		此函数初始化在先前分配的元组描述符中的单个属性结构。
 *
 * 如果 attributeName 为 NULL，attname 字段将设置为空字符串
 * （这是在我们不知道或不需要该字段名称的情况下）。 
 * 此外，一些调用者使用此函数来更改现有 tupdesc 中的与数据类型相关的字段； 
 * 他们传递 attributeName = NameStr(att->attname) 
 * 来表示不应修改 attname 字段。
 *
 * 请注意，attcollation 被设置为指定数据类型的默认值。
 * 如果需要非默认排序，请在之后使用 
 * TupleDescInitEntryCollation 插入它。
 */
void TupleDescInitEntry(TupleDesc fc_desc,
				   AttrNumber fc_attributeNumber,
				   const char *fc_attributeName,
				   Oid fc_oidtypeid,
				   int32 fc_typmod,
				   int fc_attdim)
{
	HeapTuple	fc_tuple;
	Form_pg_type fc_typeForm;
	Form_pg_attribute fc_att;

	/*
	 * 健康检查
	 */
	AssertArg(PointerIsValid(fc_desc));
	AssertArg(fc_attributeNumber >= 1);
	AssertArg(fc_attributeNumber <= fc_desc->natts);

	/*
	 * 初始化属性字段
	 */
	fc_att = TupleDescAttr(fc_desc, fc_attributeNumber - 1);

	fc_att->attrelid = 0;			/* 虚拟值 */

	/*
	 * 注意：attributeName 可以为 NULL，因为规划器并不总是
	 * 在目标列表中填充有效的 resname 值，尤其是对于 resjunk
	 * 属性。此外，如果调用者希望重用旧的 attname，则不执行任何操作。
	 */
	if (fc_attributeName == NULL)
		MemSet(NameStr(fc_att->attname), 0, NAMEDATALEN);
	else if (fc_attributeName != NameStr(fc_att->attname))
		namestrcpy(&(fc_att->attname), fc_attributeName);

	fc_att->attstattarget = -1;
	fc_att->attcacheoff = -1;
	fc_att->atttypmod = fc_typmod;

	fc_att->attnum = fc_attributeNumber;
	fc_att->attndims = fc_attdim;

	fc_att->attnotnull = false;
	fc_att->atthasdef = false;
	fc_att->atthasmissing = false;
	fc_att->attidentity = '\0';
	fc_att->attgenerated = '\0';
	fc_att->attisdropped = false;
	fc_att->attislocal = true;
	fc_att->attinhcount = 0;
	/* 变长字段在元组描述符中不存在 */

	fc_tuple = SearchSysCache1(TYPEOID, ObjectIdGetDatum(fc_oidtypeid));
	if (!HeapTupleIsValid(fc_tuple))
		elog(ERROR, "cache lookup failed for type %u", fc_oidtypeid);
	fc_typeForm = (Form_pg_type) GETSTRUCT(fc_tuple);

	fc_att->atttypid = fc_oidtypeid;
	fc_att->attlen = fc_typeForm->typlen;
	fc_att->attbyval = fc_typeForm->typbyval;
	fc_att->attalign = fc_typeForm->typalign;
	fc_att->attstorage = fc_typeForm->typstorage;
	fc_att->attcompression = InvalidCompressionMethod;
	fc_att->attcollation = fc_typeForm->typcollation;

	ReleaseSysCache(fc_tuple);
}

/*
 * TupleDescInitBuiltinEntry
 *		初始化一个不需要目录访问的元组描述符。仅支持
 *		有限范围的内置类型。
 */
void TupleDescInitBuiltinEntry(TupleDesc fc_desc,
						  AttrNumber fc_attributeNumber,
						  const char *fc_attributeName,
						  Oid fc_oidtypeid,
						  int32 fc_typmod,
						  int fc_attdim)
{
	Form_pg_attribute fc_att;

	/* 运行检查 */
	AssertArg(PointerIsValid(fc_desc));
	AssertArg(fc_attributeNumber >= 1);
	AssertArg(fc_attributeNumber <= fc_desc->natts);

	/* 初始化属性字段 */
	fc_att = TupleDescAttr(fc_desc, fc_attributeNumber - 1);
	fc_att->attrelid = 0;			/* 虚拟值 */

	/* 与 TupleDescInitEntry 不同，我们要求提供属性名称 */
	Assert(fc_attributeName != NULL);
	namestrcpy(&(fc_att->attname), fc_attributeName);

	fc_att->attstattarget = -1;
	fc_att->attcacheoff = -1;
	fc_att->atttypmod = fc_typmod;

	fc_att->attnum = fc_attributeNumber;
	fc_att->attndims = fc_attdim;

	fc_att->attnotnull = false;
	fc_att->atthasdef = false;
	fc_att->atthasmissing = false;
	fc_att->attidentity = '\0';
	fc_att->attgenerated = '\0';
	fc_att->attisdropped = false;
	fc_att->attislocal = true;
	fc_att->attinhcount = 0;
	/* 变长字段在元组描述符中不存在 */

	fc_att->atttypid = fc_oidtypeid;

	/*
	 * 我们的目标是支持足够的类型，以便让基本的内置
	 * 命令在没有目录访问的情况下工作 - 例如，这样我们即使在未连接到数据库的
	 * 进程中也可以执行某些操作。
	 */
	switch (fc_oidtypeid)
	{
		case TEXTOID:
		case TEXTARRAYOID:
			fc_att->attlen = -1;
			fc_att->attbyval = false;
			fc_att->attalign = TYPALIGN_INT;
			fc_att->attstorage = TYPSTORAGE_EXTENDED;
			fc_att->attcompression = InvalidCompressionMethod;
			fc_att->attcollation = DEFAULT_COLLATION_OID;
			break;

		case BOOLOID:
			fc_att->attlen = 1;
			fc_att->attbyval = true;
			fc_att->attalign = TYPALIGN_CHAR;
			fc_att->attstorage = TYPSTORAGE_PLAIN;
			fc_att->attcompression = InvalidCompressionMethod;
			fc_att->attcollation = InvalidOid;
			break;

		case INT4OID:
			fc_att->attlen = 4;
			fc_att->attbyval = true;
			fc_att->attalign = TYPALIGN_INT;
			fc_att->attstorage = TYPSTORAGE_PLAIN;
			fc_att->attcompression = InvalidCompressionMethod;
			fc_att->attcollation = InvalidOid;
			break;

		case INT8OID:
			fc_att->attlen = 8;
			fc_att->attbyval = FLOAT8PASSBYVAL;
			fc_att->attalign = TYPALIGN_DOUBLE;
			fc_att->attstorage = TYPSTORAGE_PLAIN;
			fc_att->attcompression = InvalidCompressionMethod;
			fc_att->attcollation = InvalidOid;
			break;

		default:
			elog(ERROR, "unsupported type %u", fc_oidtypeid);
	}
}

/*
 * TupleDescInitEntryCollation
 *
 * 为先前初始化的元组描述符条目分配非默认排序。
 */
void TupleDescInitEntryCollation(TupleDesc fc_desc,
							AttrNumber fc_attributeNumber,
							Oid fc_collationid)
{
	/*
	 * 健康检查
	 */
	AssertArg(PointerIsValid(fc_desc));
	AssertArg(fc_attributeNumber >= 1);
	AssertArg(fc_attributeNumber <= fc_desc->natts);

	TupleDescAttr(fc_desc, fc_attributeNumber - 1)->attcollation = fc_collationid;
}


/*
 * BuildDescForRelation
 *
 * 给定关系模式（ColumnDef 节点列表），构建一个 TupleDesc。
 *
 * 注意：tdtypeid 需要稍后填写。
 */
TupleDesc BuildDescForRelation(List *fc_schema)
{
	int			fc_natts;
	AttrNumber	fc_attnum;
	ListCell   *fc_l;
	TupleDesc	fc_desc;
	bool		fc_has_not_null;
	char	   *fc_attname;
	Oid			fc_atttypid;
	int32		fc_atttypmod;
	Oid			fc_attcollation;
	int			fc_attdim;

	/*
	 * 分配一个新的元组描述符
	 */
	fc_natts = list_length(fc_schema);
	fc_desc = CreateTemplateTupleDesc(fc_natts);
	fc_has_not_null = false;

	fc_attnum = 0;

	foreach(fc_l, fc_schema)
	{
		ColumnDef  *fc_entry = lfirst(fc_l);
		AclResult	fc_aclresult;
		Form_pg_attribute fc_att;

		/*
		 * 对于列表中的每个条目，从列表中获取名称和类型信息，
		 * 并让 TupleDescInitEntry 填充所需的属性信息。
		 */
		fc_attnum++;

		fc_attname = fc_entry->colname;
		typenameTypeIdAndMod(NULL, fc_entry->typeName, &fc_atttypid, &fc_atttypmod);

		fc_aclresult = pg_type_aclcheck(fc_atttypid, GetUserId(), ACL_USAGE);
		if (fc_aclresult != ACLCHECK_OK)
			aclcheck_error_type(fc_aclresult, fc_atttypid);

		fc_attcollation = GetColumnDefCollation(NULL, fc_entry, fc_atttypid);
		fc_attdim = list_length(fc_entry->typeName->arrayBounds);

		if (fc_entry->typeName->setof)
			ereport(ERROR,
					(errcode(ERRCODE_INVALID_TABLE_DEFINITION),
					 errmsg("column \"%s\" cannot be declared SETOF",
							fc_attname)));

		TupleDescInitEntry(fc_desc, fc_attnum, fc_attname,
						   fc_atttypid, fc_atttypmod, fc_attdim);
		fc_att = TupleDescAttr(fc_desc, fc_attnum - 1);

		/* 根据请求覆盖 TupleDescInitEntry 的设置 */
		TupleDescInitEntryCollation(fc_desc, fc_attnum, fc_attcollation);
		if (fc_entry->storage)
			fc_att->attstorage = fc_entry->storage;

		/* 填充 TupleDescInitEntry 未处理的其他内容 */
		fc_att->attnotnull = fc_entry->is_not_null;
		fc_has_not_null |= fc_entry->is_not_null;
		fc_att->attislocal = fc_entry->is_local;
		fc_att->attinhcount = fc_entry->inhcount;
	}

	if (fc_has_not_null)
	{
		TupleConstr *fc_constr = (TupleConstr *) palloc0(sizeof(TupleConstr));

		fc_constr->has_not_null = true;
		fc_constr->has_generated_stored = false;
		fc_constr->defval = NULL;
		fc_constr->missing = NULL;
		fc_constr->num_defval = 0;
		fc_constr->check = NULL;
		fc_constr->num_check = 0;
		fc_desc->constr = fc_constr;
	}
	else
	{
		fc_desc->constr = NULL;
	}

	return fc_desc;
}

/*
 * BuildDescFromLists
 *
 * 给定列名（作为字符串节点）的列表、列类型 OID、typmods 和排序 OID，构建一个 TupleDesc。
 *
 * 不生成约束。
 *
 * 这本质上是 BuildDescForRelation 的简化版本，用于处理返回 RECORD 的函数。
 */
TupleDesc BuildDescFromLists(List *fc_names, List *fc_types, List *fc_typmods, List *fc_collations)
{
	int			fc_natts;
	AttrNumber	fc_attnum;
	ListCell   *fc_l1;
	ListCell   *fc_l2;
	ListCell   *fc_l3;
	ListCell   *fc_l4;
	TupleDesc	fc_desc;

	fc_natts = list_length(fc_names);
	Assert(fc_natts == list_length(fc_types));
	Assert(fc_natts == list_length(fc_typmods));
	Assert(fc_natts == list_length(fc_collations));

	/*
	 * 分配一个新的元组描述符
	 */
	fc_desc = CreateTemplateTupleDesc(fc_natts);

	fc_attnum = 0;
	forfour(fc_l1, fc_names, fc_l2, fc_types, fc_l3, fc_typmods, fc_l4, fc_collations)
	{
		char	   *fc_attname = strVal(lfirst(fc_l1));
		Oid			fc_atttypid = lfirst_oid(fc_l2);
		int32		fc_atttypmod = lfirst_int(fc_l3);
		Oid			fc_attcollation = lfirst_oid(fc_l4);

		fc_attnum++;

		TupleDescInitEntry(fc_desc, fc_attnum, fc_attname, fc_atttypid, fc_atttypmod, 0);
		TupleDescInitEntryCollation(fc_desc, fc_attnum, fc_attcollation);
	}

	return fc_desc;
}
